/*
---

script: Request.JSONP.js

name: Request.JSONP

description: Defines Request.JSONP, a class for cross domain javascript via script injection.

license: MIT-style license

authors:
  - Aaron Newton
  - Guillermo Rauch

requires:
  - Core/Element
  - Core/Request

provides: [Request.JSONP]

...
*/ 

if (!Request.JSONP) {
	Request.JSONP = new Class({

		Implements: [Chain, Events, Options],

		options: {/*
			onRetry: $empty(intRetries),
			onRequest: $empty(scriptElement),
			onComplete: $empty(data),
			onSuccess: $empty(data),
			onCancel: $empty(),
			*/
			url: '',
			data: {},
			retries: 0,
			timeout: 0,
			link: 'ignore',
			callbackKey: 'callback',
			injectScript: document.head
		},

		initialize: function(options){
			this.setOptions(options);
			this.running = false;
			this.requests = 0;
			this.triesRemaining = [];
		},

		check: function(){
			if (!this.running) return true;
			switch (this.options.link){
				case 'cancel': this.cancel(); return true;
				case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
			}
			return false;
		},

		send: function(options){
			if (!$chk(arguments[1]) && !this.check(options)) return this;

			var type = $type(options), 
					old = this.options, 
					index = $chk(arguments[1]) ? arguments[1] : this.requests++;
			if (type == 'string' || type == 'element') options = {data: options};

			options = $extend({data: old.data, url: old.url}, options);

			if (!$chk(this.triesRemaining[index])) this.triesRemaining[index] = this.options.retries;
			var remaining = this.triesRemaining[index];

			(function(){
				var script = this.getScript(options);
				this.fireEvent('request', script);
				this.running = true;

				(function(){
					if (remaining){
						this.triesRemaining[index] = remaining - 1;
						if (script){
							script.destroy();
							this.send(options, index).fireEvent('retry', this.triesRemaining[index]);
						}
					} else if(this.running && script && this.options.timeout){
						script.destroy();
						this.cancel().fireEvent('failure');
					}
				}).delay(this.options.timeout, this);
			}).delay(Browser.Engine.trident ? 50 : 0, this);
			return this;
		},

		cancel: function(){
			if (!this.running) return this;
			this.running = false;
			this.fireEvent('cancel');
			return this;
		},

		getScript: function(options){
			var index = Request.JSONP.counter,
					data;
			Request.JSONP.counter++;

			switch ($type(options.data)){
				case 'element': data = document.id(options.data).toQueryString(); break;
				case 'object': case 'hash': data = Hash.toQueryString(options.data);
			}

			var src = options.url + 
				 (options.url.test('\\?') ? '&' :'?') + 
				 (options.callbackKey || this.options.callbackKey) + 
				 '=Request.JSONP.request_map.request_'+ index + 
				 (data ? '&' + data : '');

			var script = new Element('script', {type: 'text/javascript', src: src});
			Request.JSONP.request_map['request_' + index] = function(){ this.success(arguments, script); }.bind(this);
			return script.inject(this.options.injectScript);
		},

		success: function(args, script){
			if (!this.running) return false;
			if (script) script.destroy();
			this.running = false;
			this.fireEvent('complete', args).fireEvent('success', args).callChain();
		}

	});

	Request.JSONP.counter = 0;
	Request.JSONP.request_map = {};
}